# 并发通常是指同一时刻只能有一条指令执行，但是多个线程对应的指令被快速轮换地执行.(单核CPU上讨论)
# 并行是指同一时刻，有多条指令在多个处理器上同时执行，并行必须要依赖于多个处理器; 不论是从宏观上还是微观上，多个线程可以在同一时刻一起执行的.(多核CPU上讨论)
# 多个任务就是多个进程,并发执行
# 进程内的多个子任务即是多线程

# 理想的是CPU核数和进程数相同,任务执行效果最理想
## 进程池: 控制同时执行的进程数量,存在进程结束,就立即向其中补充,然后保证运行与核数一致,实现理想调度


# 多进程demo

import os
import time


def task_time(name):
    print("Run task %s  PID(%s)...." % (name, os.getpid()))
    start = time.time()
    time.sleep(3)
    end = time.time()
    print("Task %s costs %0.3f seconds" % (name, (end-start)))

# 不使用多进程
# if __name__ == "__main__":
#     print(f"Parent Process id {os.getpid()}")
#     star = time.time()
#     task_time("A")
#     task_time("B")
#     end = time.time()
#     print(f"main runs {end-star : .4f} seconds")

# 使用多进程
# if __name__ == "__main__":
#     from multiprocessing import Process
#     start = time.time()
#     print(f"Parent Process id {os.getpid()}")
#     p1 = Process(target=task_time, args=("A",))
#     p2 = Process(target=task_time, args=("B",))
#     p1.start()  # 启动进程
#     p2.start()
#     p1.join()
#     p2.join()  # 阻塞主进程,保证子进程结束后,主进程再推出
#     end = time.time()
#     print(f"main runs {end-start : .4f} seconds")

# 进程池 demo
# 不同进程之间是无法共享内存的，每个进程有自己独立的内存空间，如果要实现进程间通信，可以使用 multiprocessing 模块提供的 Queue、Pipes 等方式
if __name__ == "__main__":
    from multiprocessing import Pool, cpu_count
    cpu = cpu_count()
    start = time.time()
    print(f"Parent Process id {os.getpid()}")
    print(f"CPU numbers are {cpu}")
    p = Pool(cpu) # 根据核数创建进程池
    for i in range(cpu*2):
        p.apply_async(task_time, args=(i,)) # 异步执行任务
    print("Waiting for ending")
    p.close() # 关闭进程池,不再接收新的进程
    p.join()
    end = time.time()
    print(f"main runs {end-start : .4f} seconds")



